﻿using Chaotic.Core.Extension;
using Chaotic.DataBase.DBAccessLibrary.Interface;
using System.Text;
using System.Text.RegularExpressions;
using System.Windows.Media;
using Telerik.Windows.Controls.SyntaxEditor.Taggers;
using Telerik.Windows.Controls.SyntaxEditor.Tagging.Taggers;
using Telerik.Windows.Controls.SyntaxEditor.UI;
using Telerik.Windows.SyntaxEditor.Core.Tagging;
using Telerik.Windows.SyntaxEditor.Core.Text;
using static System.Net.Mime.MediaTypeNames;

namespace Chaotic.Module.DBTool.Common
{
    public class CustomSqlTagger : WordTaggerBase
    {
        private string stringRegexPattern = "'(?:[^'\\\\]|\\\\.)*'|\"(?:[^\"\\\\]|\\\\.)*\"";
        private ISqlKeyword _keyword;
        char _lastStartChar = char.MinValue;
        public ISqlKeyword SqlKeywords
        {
            get => _keyword;
            set
            {
                if(value != _keyword)
                {
                    _keyword = value;
                    Init();
                }
            }
        }

        private readonly Dictionary<string, Telerik.Windows.SyntaxEditor.Core.Tagging.ClassificationType> WordsToClassificationType = new ();

        public CustomSqlTagger(Telerik.Windows.Controls.RadSyntaxEditor editor, ISqlKeyword sqlKeyword)
        : base(editor)
        {
            SqlKeywords = sqlKeyword;

            if (!editor.TaggersRegistry.IsTaggerRegistered(this))
            {
                editor.TaggersRegistry.RegisterTagger(this);
            }


            //数字 也是黑色
            editor.TextFormatDefinitions.AddLast(Telerik.Windows.SyntaxEditor.Core.Tagging.ClassificationTypes.NumberLiteral,
                new TextFormatDefinition(new SolidColorBrush(Colors.Black)));
            //操作符号 灰色
            editor.TextFormatDefinitions.AddLast(Telerik.Windows.SyntaxEditor.Core.Tagging.ClassificationTypes.Operator,
                new TextFormatDefinition(new SolidColorBrush(Colors.Gray)));
            //紫色
            editor.TextFormatDefinitions.AddLast(Telerik.Windows.SyntaxEditor.Core.Tagging.ClassificationTypes.PreprocessorKeyword,
                new TextFormatDefinition(new SolidColorBrush(Colors.Purple)));
        
        }

        public void Init()
        {
            WordsToClassificationType.Clear();
            foreach (var keyword in SqlKeywords.Keywords.Distinct())
            {
                WordsToClassificationType.Add(keyword, 
                    Telerik.Windows.SyntaxEditor.Core.Tagging.ClassificationTypes.Keyword);
            }

            foreach (var preprocessor in SqlKeywords.Operators.Distinct())
            {
                WordsToClassificationType.Add(preprocessor, Telerik.Windows.SyntaxEditor.Core.Tagging.ClassificationTypes.Operator);
            }

            foreach (var comment in SqlKeywords.Comments.Distinct())
            {
                WordsToClassificationType.Add(comment, Telerik.Windows.SyntaxEditor.Core.Tagging.ClassificationTypes.Comment);
            }
             
            foreach (var comment in SqlKeywords.Functions)
            {
                WordsToClassificationType.Add(comment, Telerik.Windows.SyntaxEditor.Core.Tagging.ClassificationTypes.PreprocessorKeyword);
            }

            foreach (var comment in SqlKeywords.DataTypes)
            {
                WordsToClassificationType.Add(comment, Telerik.Windows.SyntaxEditor.Core.Tagging.ClassificationTypes.StringLiteral);
            } 
        }

        protected override Dictionary<string, Telerik.Windows.SyntaxEditor.Core.Tagging.ClassificationType> GetWordsToClassificationTypes()
        {
            return WordsToClassificationType;
        }

        protected override bool TryGetClassificationType(string word, out Telerik.Windows.SyntaxEditor.Core.Tagging.ClassificationType classificationType)
        {
            //if (int.TryParse(word, out _))
            //{
            //    //classificationType = Telerik.Windows.SyntaxEditor.Core.Tagging.ClassificationTypes.NumberLiteral;
            //    return true;
            //}

            //转化成大写字母进行匹配
            return base.TryGetClassificationType(word.ToUpper(), out classificationType);
        }

        /// <summary>
        /// 当在处理行字符串的过程中某个单词被分割时会触发此事件
        /// </summary>
        /// <param name="wordCharType"></param>
        /// <param name="word"></param>
        protected override void OnWordSplit(int wordCharType, string word)
        {
            if (wordCharType == 3 && word.Length > 2)
            {
                AddWord(word, ClassificationTypes.Comment);
            }
        }

        //定义了不同的字符类型，这是实现单词拆分的关键所在。
        //一个单词被视为由相同类型的字符组成的序列。
        //1 - 空白符，2 - 标点符号或符号，0 - 字母及所有其他字符。
        protected override int GetCharType(char c)
        {
            if (c == '#' || c == '-')
            {
                return 3;
            }
          
            if (char.IsWhiteSpace(c))
            {
                return 1;
            }
            //char.IsPunctuation(c) || 
            if (char.IsSymbol(c))
            {
                return 2;
            }

            return 0;
        } 
        protected override IList<string> SplitIntoWords(string value)
        {
            List<string> list = new List<string>();
            int num = -1;
            int num2 = 0;
            bool stringStart = false;
            string text;
            for (int i = 0; i < value.Length; i++)
            {
                CheckStringStart(ref stringStart, value, i);
                int charType = GetCharType(value[i]);
                if (charType != num && !stringStart)
                { 
                    text = value.Substring(num2, i - num2); 
                    OnWordSplit(num, text);
                    list.Add(text);
                    num2 = i;
                    num = charType;
                }
            }

            text = value.Substring(num2, value.Length - num2); 
            OnWordSplit(num, text);
         
            list.Add(text);
            return list;
        }
        protected override string StringMatchingRegex
        {
            get
            {
                return stringRegexPattern;
            }
            set
            {
                stringRegexPattern = value;
            }
        }
        protected override void RebuildMultilineTags()
        {
            base.MultilineTags.Clear();
            IList<TagSpan<ClassificationTag>> tags = GetTags(base.Document, base.Document.CurrentSnapshot.GetText(), StringMatchingRegex, "(?<Comment>\\/\\*[\\s\\S]*?(\\*\\/|\\z))");
            foreach (TagSpan<ClassificationTag> item in tags)
            {
                base.MultilineTags.Add(item);
            }
            IList<TagSpan<ClassificationTag>> tags1 = GetTags(base.Document, base.Document.CurrentSnapshot.GetText(), $"(?<Operator>{StringMatchingRegex})");
            foreach (TagSpan<ClassificationTag> item in tags1)
            {
                base.MultilineTags.Add(item);
            }
        }



        private void CheckStringStart(ref bool stringStart, string line, int currIndex)
        {
            char c = line[currIndex];
            if(_lastStartChar == char.MinValue && (c == '\'' || c == '"'))
            {
                _lastStartChar = c;
            } 

            if (c == _lastStartChar)
            {
                if (!stringStart)
                {
                    stringStart = true;
                }
                else if ((currIndex > 0 && line[currIndex - 1] != '\\') || 
                         (currIndex > 1 && line[currIndex - 2] == '\\' && line[currIndex - 1] == '\\'))
                {
                    stringStart = false;
                    _lastStartChar = char.MinValue;
                }
            }
        }
       
      
        private IList<TagSpan<ClassificationTag>> GetTags(TextDocument document, string text, string stringRegexPattern, params string[] regexPatters)
        {
            string pattern = stringRegexPattern;
            if (regexPatters.Length > 0)
            { 
                StringBuilder stringBuilder = new StringBuilder(stringRegexPattern + "|"); 
                for (int i = 0; i < regexPatters.Length; i++)
                {
                    stringBuilder.Append("{" + i + "}");
                    if (i != regexPatters.Length - 1)
                    {
                        stringBuilder.Append("|");
                    }
                }
                pattern = string.Format(stringBuilder.ToString(), regexPatters);
            }

            
            Regex regex = new Regex(pattern, RegexOptions.IgnoreCase | RegexOptions.Multiline | RegexOptions.ExplicitCapture | RegexOptions.Compiled);
            MatchCollection matchCollection = regex.Matches(text);
            string[] array = new string[2] { "Comment", "Operator" };
            List<TagSpan<ClassificationTag>> list = new List<TagSpan<ClassificationTag>>();
            foreach (Match item2 in matchCollection)
            {
                string[] array2 = array;
                foreach (string text2 in array2)
                {
                    if (item2.Groups[text2].Success)
                    {
                        int index = item2.Index;
                        int length = item2.Length;
                        TextSnapshot currentSnapshot = document.CurrentSnapshot;
                        TagSpan<ClassificationTag> item = CreateTagSpan(currentSnapshot, index, length, text2);
                        list.Add(item);
                    }
                }
            }

            return list;
        }
        private TagSpan<ClassificationTag> CreateTagSpan(TextSnapshot snapshot, int start, int len, string description)
        {
            TextSnapshotSpan textSnapshotSpan = new TextSnapshotSpan(snapshot, new Span(start, len));
            ClassificationTag tag = new ClassificationTag(GetClassificationType(description));
            return new TagSpan<ClassificationTag>(textSnapshotSpan, tag);
        }
        private ClassificationType GetClassificationType(string description)
        {
            if (description == "Comment")
                return ClassificationTypes.Comment;
            else if (description == "Operator")
                return ClassificationTypes.StringLiteral;
            return null;
        }  
    }
}
